package org.coco.mvc.handle;

import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import org.coco.core.annotation.ResponseBody;
import org.coco.mvc.RequestProcessorChain;
import org.coco.mvc.annotation.RequestBody;
import org.coco.mvc.render.impl.DefaultResultRender;
import org.coco.mvc.render.impl.InternalErrorResultRender;
import org.coco.mvc.render.impl.JsonResultRender;
import org.coco.mvc.render.impl.ViewResultRender;
import org.coco.util.CommonUtil;
import org.coco.util.SetterValueUtil;
import org.coco.util.ValidationUtil;

import javax.servlet.ServletInputStream;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Iterator;
import java.util.Map;

/**
 * 处理POST请求的处理器
 */
public class PostHandleMapping {

    public boolean doPost(RequestProcessorChain requestProcessorChain, Map<String, HandleMapping> requestMappingMap
    ,String requestPath) throws Exception{
        //如果是POST请求，就需要获取Body中的内容
        BufferedReader br = new BufferedReader(
                new InputStreamReader((ServletInputStream) requestProcessorChain.getRequest().getInputStream(), "utf-8"));
        StringBuffer sb = new StringBuffer("");
        String temp;
        while ((temp = br.readLine()) != null) {
            sb.append(temp);
        }
        br.close();
        //得到请求体Body中的内容
        String bodyContent = sb.toString();
        //将body中的内容进行json化
        JsonParser jsonParser = new JsonParser();
        JsonObject jsonObject = jsonParser.parse(bodyContent).getAsJsonObject();
        //获取到我们需要执行的方法
        HandleMapping handleMappingResult = requestMappingMap.get(requestPath);
        //从获取到的handleMappingResult得到需要执行那个类的那个方法
        Class<?> clazz = handleMappingResult.getClazz();
        String invokeMethodName = handleMappingResult.getMethod();
        //获取该类所有的方法
        Method[] methods = clazz.getMethods();
        boolean isHaveMethod = false;
        for(Method method : methods) {
            //排除Object类中一些自带的方法
            if (CommonUtil.isMethodName(method)) {
                continue;
            }
            if(invokeMethodName.equals(method.getName())) {
                //说明已经匹配到客户端需要执行的方法了
                isHaveMethod = true;
                Object res = clazz.newInstance();
                //获取该方法中的所有参数，并且解析@RequestBody注解
                Parameter[] parameters = method.getParameters();
                //获取该方法参数的所有对应的类型
                Class<?>[] parameterTypes = method.getParameterTypes();
                if(!ValidationUtil.isEmpty(parameters)) {
                    Parameter parameter = null;
                    Object object = null;
                    boolean requestBodyFlag = false;
                    for (int i = 0; i < parameters.length; i ++) {
                        parameter = parameters[i];
                        //判断一下该参数是否有@RequestBody，没有的话就抛出异常
                        //springmvc是不支持一个方法使用多个@RequestBody注解的，因为一个request只有一个request Body
                        if(parameter.isAnnotationPresent(RequestBody.class)){
                            if(requestBodyFlag) {
                                requestProcessorChain.setResultRender(
                                        new InternalErrorResultRender(new RuntimeException("一个方法不能使用一个以上的@RequestBody注解")));
                                return true;
                            }
                            //判断该参数是否是我们自定义的类
                            //我们可以通过获取该参数的classLoader
                            ClassLoader classLoader = parameterTypes[i].getClassLoader();
                            //获取该类的所有的属性
                            Field[] declaredFields = parameterTypes[i].newInstance().getClass().getDeclaredFields();
                            //如果classLoader ！= null 说明是我们自定义的类
                            if(classLoader != null) {
                                if((ValidationUtil.isEmpty(declaredFields) && !ValidationUtil.isEmpty(bodyContent))
                                        || (!ValidationUtil.isEmpty(declaredFields) && ValidationUtil.isEmpty(bodyContent))) {
                                    //就是说我们自定义的类中没有属性，但是前端又传过来了，直接报错
                                    //或者我们自定义的类有属性，但是前台没有传过来
                                    requestProcessorChain.setResultRender(
                                            new InternalErrorResultRender(new RuntimeException("参数错误")));
                                    return true;
                                }else if(ValidationUtil.isEmpty(declaredFields) && ValidationUtil.isEmpty(bodyContent)) {

                                }else {
                                    boolean filedFlag = false;
                                    Iterator<Map.Entry<String, JsonElement>> iterator = jsonObject.entrySet().iterator();
                                    while (iterator.hasNext()) {
                                        filedFlag = false;
                                        Map.Entry<String, JsonElement> entry = iterator.next();
                                        for(Field field : declaredFields) {
                                            String key = entry.getKey();
                                            if(key.equals(field.getName())) {
                                                filedFlag = true;
                                                continue;
                                            }
                                        }
                                        if(!filedFlag) {
                                            break;
                                        }
                                    }

                                    if(filedFlag) {
                                        //参数对应上了，然后就可以对象属性进行赋值了
                                        String name = parameterTypes[i].getName();
                                        Class<?> aClass = Class.forName(name);
                                        object = aClass.newInstance();
                                        Field[] fields = parameterTypes[i].newInstance().getClass().getDeclaredFields();
                                        Iterator<Map.Entry<String, JsonElement>> keyIterator = jsonObject.entrySet().iterator();
                                        while (keyIterator.hasNext()){
                                            Map.Entry<String, JsonElement> entry = keyIterator.next();
                                            for(int t = 0; t < fields.length; t ++) {
                                                String key = entry.getKey();
                                                JsonElement value = entry.getValue();
                                                if(key.equals(fields[t].getName())) {
                                                    Class typeClass = fields[t].getType();
                                                    fields[t].setAccessible(true);
                                                    if(typeClass == Integer.class || typeClass == int.class) {
                                                        SetterValueUtil.setter(object,fields[t].getName(),value.getAsInt(),Integer.class);
                                                    }else if(typeClass == String.class) {
                                                        SetterValueUtil.setter(object,fields[t].getName(),value.getAsString(),String.class);
                                                    }
                                                    //todo 还有其它类型的
                                                }

                                            }
                                        }
                                        //todo

                                    }else {
                                        //说明客户端传的参数与我们自定义的对象中的属性值对不上
                                        requestProcessorChain.setResultRender(
                                                new InternalErrorResultRender(new RuntimeException("参数对不上")));
                                        return true;
                                    }

                                }
                            }else {
                                //不是客户自定义的类，直接抛出异常
                                requestProcessorChain.setResultRender(
                                        new InternalErrorResultRender(new RuntimeException("不是自定义的类")));
                                return true;
                            }
                            requestBodyFlag = true;
                        }else {
                            //说明该参数是没有@RequestBody注解的
                            //todo 需要判断下是否能有别的参数，比如HttpServletRequest,HttpSession参数就不需要有注解了等
                            //todo 暂时这里就不做任何处理了
                            requestProcessorChain.setResultRender(
                                    new InternalErrorResultRender(new RuntimeException("method "+method.getName()+" need params: "+ parameter.getName())));
                            return true;
                        }
                    }

                    Class<?> returnType = method.getReturnType();
                    if(returnType == void.class) {
                        //也就是该方法没有返回值,直接执行完方法
                        method.invoke(clazz.newInstance(),object);
                        requestProcessorChain.setResultRender(new DefaultResultRender());
                    }else {
                        //判断该方法或者该类上是否有@ResponseBody注解
                        //得到该方法的返回值
                        Object methodReturnValue = method.invoke(clazz.newInstance(),object);
                        if(clazz.isAnnotationPresent(ResponseBody.class) || method.isAnnotationPresent(ResponseBody.class)) {
                            requestProcessorChain.setResultRender(new JsonResultRender(methodReturnValue));
                        }else {
                            //说明该方法需要匹配一个页面返回
                            //暂时这里就不做任何处置,比如返回 xx 那就返回 xx.html
                            requestProcessorChain.setResultRender(new ViewResultRender(methodReturnValue));
                        }
                    }
                }
            }
        }
        return true;
    }
}
